using System;
using System.Diagnostics.CodeAnalysis;

namespace Bordecal.ImportsSorter
{
	/// <summary>
	/// Helper class used to provide additional information about syntax element use in a code language.
	/// </summary>
	internal sealed class CodeLanguage
	{
		#region Fields

		/// <summary>
		/// The type of language being represented.
		/// </summary>
		private readonly LanguageType languageType;

		/// <summary>
		/// The prefix used for inline comments in the language (e.g.: "//" in C#, "'" in VB).
		/// </summary>
		private readonly string inlineCommentPrefix;

		/// <summary>
		/// The character(s) used to separate parts of a namespace in the language (e.g.: "." in C#, "::" in C++).
		/// </summary>
		private readonly string namespacePartSeparator;

		/// <summary>
		/// Prefixes before which a namespace should be truncated.
		/// </summary>
		/// <remarks>
		/// This is used mainly for removal of namespace qualifiers (e.g.: "global::" in C#).
		/// </remarks>
		private readonly string[] namespaceTruncationPrefixes;

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the CodeLanguage class.
		/// </summary>
		/// <remarks>Only the C#, VB, J#, and C++ languages are supported by this class.</remarks>
		/// <param name="languageName">The name of the code language (as returned by the EnvDTE.Document.Language property).</param>
		internal CodeLanguage(string languageName)
		{
			if (languageName == null)
				throw new ArgumentNullException("languageName");

			this.languageType = CodeLanguage.LanguageTypeFromName(languageName);

			switch (this.languageType)
			{
				case LanguageType.CSharp:
					this.inlineCommentPrefix = "//";
					this.namespaceTruncationPrefixes = new string[] { "::" };
					this.namespacePartSeparator = ".";
					break;
				case LanguageType.VisualBasic:
					this.inlineCommentPrefix = "'";
					this.namespaceTruncationPrefixes = new string[0];
					this.namespacePartSeparator = ".";
					break;
				case LanguageType.CPlusPlus:
					this.inlineCommentPrefix = "//";
					this.namespaceTruncationPrefixes = new string[0];
					this.namespacePartSeparator = "::";
					break;
				case LanguageType.JSharp:
					this.inlineCommentPrefix = "//";
					this.namespaceTruncationPrefixes = new string[0];
					this.namespacePartSeparator = ".";
					break;
				default:
					throw new ArgumentOutOfRangeException("languageName");
			}
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets the prefix used for inline comments in the language (e.g.: "//" in C#, "'" in VB).
		/// </summary>
		internal string InlineCommentStart
		{
			get
			{
				return this.inlineCommentPrefix;
			}
		}

		/// <summary>
		/// Gets the type of language being represented.
		/// </summary>
		internal LanguageType LanguageType
		{
			get
			{
				return this.languageType;
			}
		}

		/// <summary>
		/// Gets the prefixes before which a namespace should be truncated.
		/// </summary>
		/// <remarks>
		/// This is used mainly for removal of namespace qualifiers (e.g.: "global::" in C#).
		/// </remarks>
		internal string[] NamespaceTruncationPrefixes
		{
			get
			{
				string[] retVal = new string[this.namespaceTruncationPrefixes.Length];
				this.namespaceTruncationPrefixes.CopyTo(retVal, 0);
				
				return retVal;
			}
		}

		/// <summary>
		/// Gets the character(s) used to separate parts of a namespace in the language (e.g.: "." in C#, "::" in C++).
		/// </summary>
		internal string NamespacePartSeparator
		{
			get
			{
				return namespacePartSeparator;
			}
		}

		#endregion

		#region Methods

		internal static LanguageType LanguageTypeFromName(string languageName)
		{
			LanguageType retVal = LanguageType.Unknown;

			switch (languageName)
			{
				case "CSharp":
					retVal = LanguageType.CSharp;
					break;
				case "Basic":
					retVal = LanguageType.VisualBasic;
					break;
				case "C/C++":
					retVal = LanguageType.CPlusPlus;
					break;
				case "Visual JSharp":
					retVal = LanguageType.JSharp;
					break;
			}

			return retVal;
		}

		#endregion
	}
}